//	VirtualDub - Video processing and capture application
//	System library component
//	Copyright (C) 1998-2004 Avery Lee, All Rights Reserved.
//
//	Beginning with 1.6.0, the VirtualDub system library is licensed
//	differently than the remainder of VirtualDub.  This particular file is
//	thus licensed as follows (the "zlib" license):
//
//	This software is provided 'as-is', without any express or implied
//	warranty.  In no event will the authors be held liable for any
//	damages arising from the use of this software.
//
//	Permission is granted to anyone to use this software for any purpose,
//	including commercial applications, and to alter it and redistribute it
//	freely, subject to the following restrictions:
//
//	1.	The origin of this software must not be misrepresented; you must
//		not claim that you wrote the original software. If you use this
//		software in a product, an acknowledgment in the product
//		documentation would be appreciated but is not required.
//	2.	Altered source versions must be plainly marked as such, and must
//		not be misrepresented as being the original software.
//	3.	This notice may not be removed or altered from any source
//		distribution.

#include "stdafx.h"
#include <math.h>

#include <vd2/system/int128.h>

#ifndef _M_AMD64
	void __declspec(naked) __cdecl vdasm_uint128_add(uint64 dst[2], const uint64 x[2], const uint64 y[2]) {
		__asm {
			push	ebx

			mov		ebx, [esp+16]
			mov		ecx, [esp+12]
			mov		edx, [esp+8]

			mov		eax, [ecx+0]
			add		eax, [ebx+0]
			mov		[edx+0],eax
			mov		eax, [ecx+4]
			adc		eax, [ebx+4]
			mov		[edx+4],eax
			mov		eax, [ecx+8]
			adc		eax, [ebx+8]
			mov		[edx+8],eax
			mov		eax, [ecx+12]
			adc		eax, [ebx+12]
			mov		[edx+12],eax

			pop		ebx
			ret
		}
	}

	void __declspec(naked) __cdecl vdasm_uint128_sub(uint64 dst[2], const uint64 x[2], const uint64 y[2]) {
		__asm {
			push	ebx

			mov		ebx, [esp+16]
			mov		ecx, [esp+12]
			mov		edx, [esp+8]

			mov		eax, [ecx+0]
			sub		eax, [ebx+0]
			mov		[edx+0],eax
			mov		eax, [ecx+4]
			sbb		eax, [ebx+4]
			mov		[edx+4],eax
			mov		eax, [ecx+8]
			sbb		eax, [ebx+8]
			mov		[edx+8],eax
			mov		eax, [ecx+12]
			sbb		eax, [ebx+12]
			mov		[edx+12],eax

			pop		ebx
			ret
		}
	}

	void __declspec(naked) vdint128::setSquare(sint64 v) {
		__asm {
			push	edi
			push	esi
			push	ebx
			mov		eax, [esp+20]
			cdq
			mov		esi, eax
			mov		eax, [esp+16]
			xor		eax, edx
			xor		esi, edx
			sub		eax, edx
			sbb		esi, edx
			mov		ebx, eax
			mul		eax
			mov		[ecx], eax
			mov		edi, edx
			mov		eax, ebx
			mul		esi
			mov		ebx, 0
			add		eax, eax
			adc		edx, edx
			add		eax, edi
			adc		edx, 0
			mov		edi, edx
			adc		ebx, 0
			mov		[ecx+4], eax
			mov		eax, esi
			mul		esi
			add		eax, edi
			adc		edx, ebx
			mov		[ecx+8], eax
			mov		[ecx+12], edx
			pop		ebx
			pop		esi
			pop		edi
			ret		8
		}
	}

	const vdint128 __declspec(naked) vdint128::operator<<(int v) const {
		__asm {
			push	ebp
			push	ebx
			push	esi
			push	edi

			mov		esi,ecx
			mov		edx,[esp+20]

			mov		ecx,[esp+24]
			cmp		ecx,128
			jae		zeroit

			mov		eax,[esi+12]
			mov		ebx,[esi+8]
			mov		edi,[esi+4]
			mov		ebp,[esi]

	dwordloop:
			cmp		ecx,32
			jb		bits

			mov		eax,ebx
			mov		ebx,edi
			mov		edi,ebp
			xor		ebp,ebp
			sub		ecx,32
			jmp		short dwordloop

	bits:
			shld	eax,ebx,cl
			shld	ebx,edi,cl
			mov		[edx+12],eax
			mov		[edx+8],ebx
			shld	edi,ebp,cl

			shl		ebp,cl
			mov		[edx+4],edi
			mov		[edx],ebp

			pop		edi
			pop		esi
			pop		ebx
			pop		ebp
			mov		eax,[esp+4]
			ret		8

	zeroit:
			xor		eax,eax
			mov		[edx+0],eax
			mov		[edx+4],eax
			mov		[edx+8],eax
			mov		[edx+12],eax

			pop		edi
			pop		esi
			pop		ebx
			pop		ebp
			mov		eax,[esp+4]
			ret		8
		}
	}

	const vdint128 __declspec(naked) vdint128::operator>>(int v) const {
		__asm {
			push	ebp
			push	ebx
			push	esi
			push	edi

			mov		esi,ecx
			mov		edx,[esp+20]

			mov		eax,[esi+12]
			mov		ecx,[esp+24]
			cmp		ecx,127
			jae		clearit

			mov		ebx,[esi+8]
			mov		edi,[esi+4]
			mov		ebp,[esi]

	dwordloop:
			cmp		ecx,32
			jb		bits

			mov		ebp,edi
			mov		edi,ebx
			mov		ebx,eax
			sar		eax,31
			sub		ecx,32
			jmp		short dwordloop

	bits:
			shrd	ebp,edi,cl
			shrd	edi,ebx,cl
			mov		[edx],ebp
			mov		[edx+4],edi
			shrd	ebx,eax,cl

			sar		eax,cl
			mov		[edx+8],ebx
			mov		[edx+12],eax

			pop		edi
			pop		esi
			pop		ebx
			pop		ebp
			mov		eax,[esp+4]
			ret		8

	clearit:
			sar		eax, 31
			mov		[edx+0],eax
			mov		[edx+4],eax
			mov		[edx+8],eax
			mov		[edx+12],eax

			pop		edi
			pop		esi
			pop		ebx
			pop		ebp
			mov		eax,[esp+4]
			ret		8
		}
	}

	const vduint128 __declspec(naked) vduint128::operator<<(int v) const {
		__asm {
			push	ebp
			push	ebx
			push	esi
			push	edi

			mov		esi,ecx
			mov		edx,[esp+20]

			mov		ecx,[esp+24]
			cmp		ecx,128
			jae		zeroit

			mov		eax,[esi+12]
			mov		ebx,[esi+8]
			mov		edi,[esi+4]
			mov		ebp,[esi]

	dwordloop:
			cmp		ecx,32
			jb		bits

			mov		eax,ebx
			mov		ebx,edi
			mov		edi,ebp
			xor		ebp,ebp
			sub		ecx,32
			jmp		short dwordloop

	bits:
			shld	eax,ebx,cl
			shld	ebx,edi,cl
			mov		[edx+12],eax
			mov		[edx+8],ebx
			shld	edi,ebp,cl

			shl		ebp,cl
			mov		[edx+4],edi
			mov		[edx],ebp

			pop		edi
			pop		esi
			pop		ebx
			pop		ebp
			mov		eax,[esp+4]
			ret		8

	zeroit:
			xor		eax,eax
			mov		[edx+0],eax
			mov		[edx+4],eax
			mov		[edx+8],eax
			mov		[edx+12],eax

			pop		edi
			pop		esi
			pop		ebx
			pop		ebp
			mov		eax,[esp+4]
			ret		8
		}
	}

	const vduint128 __declspec(naked) vduint128::operator>>(int v) const {
		__asm {
			push	ebp
			push	ebx
			push	esi
			push	edi

			mov		esi,ecx
			mov		edx,[esp+20]

			mov		eax,[esi+12]
			mov		ecx,[esp+24]
			cmp		ecx,127
			jae		clearit

			mov		ebx,[esi+8]
			mov		edi,[esi+4]
			mov		ebp,[esi]

	dwordloop:
			cmp		ecx,32
			jb		bits

			mov		ebp,edi
			mov		edi,ebx
			mov		ebx,eax
			xor		eax,eax
			sub		ecx,32
			jmp		short dwordloop

	bits:
			shrd	ebp,edi,cl
			shrd	edi,ebx,cl
			mov		[edx],ebp
			mov		[edx+4],edi
			shrd	ebx,eax,cl

			shr		eax,cl
			mov		[edx+8],ebx
			mov		[edx+12],eax

			pop		edi
			pop		esi
			pop		ebx
			pop		ebp
			mov		eax,[esp+4]
			ret		8

	clearit:
			sar		eax, 31
			mov		[edx+0],eax
			mov		[edx+4],eax
			mov		[edx+8],eax
			mov		[edx+12],eax

			pop		edi
			pop		esi
			pop		ebx
			pop		ebp
			mov		eax,[esp+4]
			ret		8
		}
	}
#endif

const vdint128 vdint128::operator*(const vdint128& x) const {
	vdint128 X = x.abs();
	vdint128 Y = abs();

	vduint128 bd(VDUMul64x64To128(X.q[0], Y.q[0]));

	bd.q[1] += X.q[0]*Y.q[1] + X.q[1]*Y.q[0];

	return (q[1]^x.q[1])<0 ? -(const vdint128&)bd : (const vdint128&)bd;
}

vdint128::operator double() const {
	return (double)(unsigned long)q[0]
		+ ldexp((double)(unsigned long)((unsigned __int64)q[0]>>32), 32)
		+ ldexp((double)q[1], 64);
}

/////////////////////////////////////////////////////////////////////////////

const vduint128 vduint128::operator*(const vduint128& x) const {
	vduint128 result(VDUMul64x64To128(q[0], x.q[0]));

	result.q[1] += q[0]*x.q[1] + q[1]*x.q[0];

	return result;
}

#ifdef _M_IX86
	vduint128 __declspec(naked) __cdecl VDUMul64x64To128(uint64 x, uint64 y) {
		__asm {
			mov		ecx,[esp+4]

			mov		eax,[esp+8]
			mul		dword ptr [esp+16]		;EDX:EAX = BD
			mov		[ecx+0],eax
			mov		[ecx+4],edx

			mov		eax,[esp+12]
			mul		dword ptr [esp+20]		;EDX:EAX = AC
			mov		[ecx+8],eax
			mov		[ecx+12],edx

			mov		eax,[esp+8]
			mul		dword ptr [esp+20]		;EDX:EAX = BC
			add		[ecx+4],eax
			adc		[ecx+8],edx
			adc		[ecx+12], 0

			mov		eax,[esp+12]
			mul		dword ptr [esp+16]		;EDX:EAX = AD
			add		[ecx+4],eax
			adc		[ecx+8],edx
			adc		[ecx+12], 0

			mov		eax, ecx
			ret
		}
	}
#endif

uint64 VDUDiv128x64To64(const vduint128& dividend, uint64 divisor, uint64& remainder) {
	vduint128 temp(dividend);
	vduint128 divisor2(divisor);

	divisor2 <<= 63;

	uint64 result = 0;
	for(int i=0; i<64; ++i) {
		result += result;
		if (temp >= divisor2) {
			temp -= divisor2;
			++result;
		}
		temp += temp;
	}

	remainder = temp.q[1];

	return result;
}
